#pragma once
#include "mutex.hpp"
#include <string>
#include <iostream>
#include <fstream>
#include <filesystem> //c++17
#include <memory>
#include<unistd.h>
#include<time.h>
namespace logmodule
{
    std::string defaultpath = "./log";   // 日志默认生成路径
    std::string defaultfile = "/log.txt"; // 日志默认生成文件

    enum class loglevel{
        DEBUG=1,
        INFO,
        WARRING,
        ERROR,
        FATAL
    };

    std::string currtime()//获取当前时间
    {
        char buf[1024];
        time_t timestamp=time(nullptr);
        struct tm time;
        localtime_r(&timestamp,&time);
        snprintf(buf,sizeof(buf),"%d-%d-%d %d:%d:%d",\
        time.tm_year+1900,time.tm_mon+1,time.tm_mday,\
        time.tm_hour,time.tm_min,time.tm_sec);
        return std::string(buf);
    }

    std::string leveltostring(loglevel level)
    {
        std::string str;
        switch (level)
        {
        case loglevel::DEBUG:str="DEBUG";
            break;
        case loglevel::ERROR:str="ERROR";
            break;
        case loglevel::FATAL:str="FATAL";
            break;
        case loglevel::WARRING:str="WARRING";
            break;
        case loglevel::INFO:str="INFO";
            break;
        }
        return str;
    }

    class LogStrategy // 接口类
    {
    public:
        virtual void LogStrategysync(std::string &message) = 0; // 刷新日志
        virtual ~LogStrategy(){}
    };

    class ConsoleLogStrategy : public LogStrategy // 继承接口类，向控制台输出日志
    {
    public:
        void LogStrategysync(std::string &message)
        {
            mutexmodule::lockgroud lock(_mutex);
            std::cerr << message << std::endl;
        }
        ~ConsoleLogStrategy()
        {
        }

    private:
        mutexmodule::mutex _mutex;
    };

    class FileLogStrategy : public LogStrategy // 继承接口类，向文件输出日志
    {
    public:
        FileLogStrategy() : _filename(defaultfile), _filepath(defaultpath)
        {
            mutexmodule::lockgroud lock(_mutex);
            if (std::filesystem::exists(_filepath))
                return;
            try
            {
                std::filesystem::create_directory(_filepath);
            }
            catch (std::filesystem::filesystem_error &e)
            {
                std::cerr << e.what() << std::endl;
            }
        }
        FileLogStrategy(const std::string &filepath, std::string &filename) : _filename(filename), _filepath(filepath)
        {
            mutexmodule::lockgroud lock(_mutex);
            if (std::filesystem::exists(_filepath))
                return;
            try
            {
                std::filesystem::create_directory(_filepath);
            }
            catch (std::filesystem::filesystem_error &e)
            {
                std::cerr << e.what() << std::endl;
            }
        }
        void LogStrategysync(std::string &message)
        {
            mutexmodule::lockgroud lock(_mutex);
            std::string file = _filepath + _filename;
            std::ofstream out(file.c_str(), std::ofstream::app);
            out << message << '\n';
            out.close();
        }

    private:
        std::string _filepath;
        std::string _filename;
        mutexmodule::mutex _mutex;
    };

    class Logger
    {
    public:
        Logger(){
            _strategy=std::make_shared<ConsoleLogStrategy>();//默认策略是是使用控制台输出日志信息
        }
        void ConverConsoleStrategy(){
            _strategy=std::make_shared<ConsoleLogStrategy>();
        }
        void ConverFileStrategy(std::string filepath,std::string filename){
            _strategy=std::make_shared<FileLogStrategy>(filepath,filename);
        }
        void ConverFileStrategy(){
            _strategy=std::make_shared<FileLogStrategy>();
        }
        //日志信息封装成类
        class LogMessage
        {
        public:
            LogMessage(loglevel level,std::string filename,int line,Logger& log)//构造出message的大部分固定内容
            :_time(currtime())
            ,_level(level)
            ,_pid(::getpid())
            ,_filename(filename)
            ,_line(line)
            ,_log(log)
            {
                std::stringstream ss;
                ss<<'['<<_time<<"] ["<<leveltostring(_level)<<"] ["<<_pid<<"] ["<<_filename<<"] ["<<_line<<"] -";
                message=ss.str();
            }

            template<class T>
            LogMessage& operator<<(const T& data)
            {
                std::stringstream ss;
                ss<<data;
                message+=ss.str();
                return *this;
            }

            ~LogMessage(){
                _log._strategy->LogStrategysync(message);
            }
        private:
            std::string _time;//当前时间
            loglevel _level;
            pid_t _pid;
            std::string _filename;
            int _line;
            std::string message;
            Logger& _log;
        };

        LogMessage operator()(loglevel level,std::string filename,int line)
        {
            return LogMessage(level,filename,line,*this);
        }
        ~Logger()=default;
    private:
        std::shared_ptr<LogStrategy> _strategy;
    };

    Logger logger;
}
#define LOG(LEVEL) (logmodule::logger(LEVEL,__FILE__,__LINE__))
#define CONVERT_CONSOLE_LOG() logmodule::logger.ConverConsoleStrategy()
#define CONVERT_FILE_LOG() logmodule::logger.ConverFileStrategy()
#define DEBUG logmodule::loglevel::DEBUG
#define INFO logmodule::loglevel::INFO
#define WARRING logmodule::loglevel::WARRING
#define ERROR logmodule::loglevel::ERROR
#define FATAL logmodule::loglevel::FATAL